/***********************************************************************
 *            Project: CoreCms
 *        ProjectName: 核心内容管理系统                                
 *                Web: https://www.corecms.net                      
 *             Author: 大灰灰                                          
 *              Email: jianweie@163.com                                
 *         CreateTime: 2021/1/31 21:45:10
 *        Description: 暂无
 ***********************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using CoreCms.Net.Caching.AccressToken;
using CoreCms.Net.Configuration;
using CoreCms.Net.IRepository;
using CoreCms.Net.IRepository.UnitOfWork;
using CoreCms.Net.IServices;
using CoreCms.Net.Loging;
using CoreCms.Net.Model.Entities;
using CoreCms.Net.Model.Entities.Expression;
using CoreCms.Net.Model.ViewModels.Basics;
using CoreCms.Net.Model.ViewModels.UI;
using CoreCms.Net.Utility.Extensions;
using CoreCms.Net.Utility.Helper;
using CoreCms.Net.WeChat.Service.HttpClients;
using Essensoft.Paylink.WeChatPay;
using Essensoft.Paylink.WeChatPay.V2;
using Essensoft.Paylink.WeChatPay.V2.Request;
using Essensoft.Paylink.WeChatPay.V3.Domain;
using Essensoft.Paylink.WeChatPay.V3.Request;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using SqlSugar;
using Yitter.IdGenerator;
using Microsoft.AspNetCore.Hosting;


namespace CoreCms.Net.Services
{
    /// <summary>
    /// 用户提现记录表 接口实现
    /// </summary>
    public class CoreCmsUserTocashServices : BaseServices<CoreCmsUserTocash>, ICoreCmsUserTocashServices
    {
        private readonly ICoreCmsUserTocashRepository _dal;
        private readonly IUnitOfWork _unitOfWork;
        private readonly IServiceProvider _serviceProvider;
        private readonly ICoreCmsUserServices _userServices;
        private readonly ICoreCmsUserBalanceServices _userBalanceServices;
        private readonly ICoreCmsUserWeChatInfoServices _weChatInfoServices;
        private readonly IWeChatPayClient _v2Client;
        private readonly Essensoft.Paylink.WeChatPay.V3.IWeChatPayClient _v3Client;
        private readonly IHttpContextAccessor _httpContextAccessor;

        private readonly IWebHostEnvironment _webHostEnvironment;
        private readonly IWeChatPayConfigServices _weChatPayConfigServices;



        public CoreCmsUserTocashServices(IUnitOfWork unitOfWork, ICoreCmsUserTocashRepository dal,
            IServiceProvider serviceProvider, ICoreCmsUserServices userServices, ICoreCmsUserBalanceServices userBalanceServices, ICoreCmsUserWeChatInfoServices weChatInfoServices, IWeChatPayClient v2Client, IHttpContextAccessor httpContextAccessor, IWebHostEnvironment webHostEnvironment, Essensoft.Paylink.WeChatPay.V3.IWeChatPayClient v3Client, IWeChatPayConfigServices weChatPayConfigServices)
        {
            this._dal = dal;
            base.BaseDal = dal;
            _unitOfWork = unitOfWork;
            _serviceProvider = serviceProvider;
            _userServices = userServices;
            _userBalanceServices = userBalanceServices;
            _weChatInfoServices = weChatInfoServices;
            _v2Client = v2Client;
            _httpContextAccessor = httpContextAccessor;
            _webHostEnvironment = webHostEnvironment;
            _v3Client = v3Client;
            _weChatPayConfigServices = weChatPayConfigServices;
        }

        /// <summary>
        /// 提现申请
        /// </summary>
        /// <returns></returns>
        public async Task<WebApiCallBack> Tocash(int userId, decimal money, int bankCardsId)
        {
            var jm = new WebApiCallBack();

            using var container = _serviceProvider.CreateScope();

            var settingServices = container.ServiceProvider.GetService<ICoreCmsSettingServices>();
            var userServices = container.ServiceProvider.GetService<ICoreCmsUserServices>();
            var userBankCardServices = container.ServiceProvider.GetService<ICoreCmsUserBankCardServices>();
            var balanceServices = container.ServiceProvider.GetService<ICoreCmsUserBalanceServices>();

            var allConfigs = await settingServices.GetConfigDictionaries();

            //最小提现金额
            var tocashMoneyLow = CommonHelper.GetConfigDictionary(allConfigs, SystemSettingConstVars.TocashMoneyLow).ObjectToDecimal((decimal)0.01);
            //每日提现上线
            var tocashMoneyLimit = CommonHelper.GetConfigDictionary(allConfigs, SystemSettingConstVars.TocashMoneyLimit).ObjectToDecimal(0);
            //提现手续费
            var tocashMoneyRate = CommonHelper.GetConfigDictionary(allConfigs, SystemSettingConstVars.TocashMoneyRate).ObjectToDecimal(0);
            //最低提现金额
            if (money < tocashMoneyLow)
            {
                jm.msg = "提现最低不能少于" + tocashMoneyLow + "元";
                return jm;
            }
            //每日提现上限，默认0不限制
            if (tocashMoneyLimit > 0)
            {
                var dt = DateTime.Now;
                var starTime = new DateTime(dt.Year, dt.Month, dt.Day, 0, 0, 0);
                var endTime = new DateTime(dt.Year, dt.Month, dt.Day, 23, 59, 59);
                //判断历史提现金额  
                var todayMoney = await _dal.GetSumAsync(p => p.createTime >= starTime && p.createTime <= endTime && p.userId == userId, p => p.money);
                todayMoney = todayMoney + money; //历史今天提现加上本次提现;
                if (todayMoney > tocashMoneyLimit)
                {
                    jm.msg = "每日提现不能超过" + tocashMoneyLimit + "元";
                    return jm;
                }
            }
            var userInfo = await userServices.QueryByIdAsync(userId);
            if (userInfo == null)
            {
                jm.msg = GlobalErrorCodeVars.Code11004;
                return jm;
            }
            if (money > userInfo.balance)
            {
                jm.msg = GlobalErrorCodeVars.Code11015;
                return jm;
            }
            // 计算提现服务费(金额)
            var cateMoney = money * (tocashMoneyRate / 100);
            if (cateMoney + money > userInfo.balance)
            {
                jm.msg = GlobalErrorCodeVars.Code11015;
                return jm;
            }
            //获取银行卡信息
            var bankcardsInfo = await userBankCardServices.QueryByClauseAsync(p => p.userId == userId && p.id == bankCardsId);
            if (bankcardsInfo == null)
            {
                jm.msg = GlobalErrorCodeVars.Code11016;
                return jm;
            }
            var cashModel = new CoreCmsUserTocash();
            cashModel.userId = userId;
            cashModel.money = money;
            cashModel.bankName = bankcardsInfo.bankName;
            cashModel.bankCode = bankcardsInfo.bankCode;
            cashModel.bankAreaId = bankcardsInfo.bankAreaId;
            cashModel.accountBank = bankcardsInfo.accountBank;
            cashModel.accountName = bankcardsInfo.accountName;
            cashModel.cardNumber = bankcardsInfo.cardNumber;
            cashModel.status = (int)GlobalEnumVars.UserTocashStatus.待审核;
            cashModel.withdrawals = cateMoney;
            cashModel.createTime = DateTime.Now;
            cashModel.type = (int)GlobalEnumVars.UserTocashType.银行线下转账;

            var res = await _dal.InsertAsync(cashModel);
            if (res > 0)
            {
                var change = await balanceServices.Change(userId, (int)GlobalEnumVars.UserBalanceSourceTypes.Tocash, money, res.ToString(), cateMoney);
                jm.status = change.status;
                jm.msg = jm.status ? "提现申请成功" : "提现申请失败";
                jm.data = change.data;
                jm.otherData = change.otherData;
            }
            else
            {
                jm.msg = "提现申请失败";
            }

            return jm;
        }


        /// <summary>
        /// 获取用户提现列表记录
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="page"></param>
        /// <param name="limit"></param>
        /// <param name="status"></param>
        /// <returns></returns>
        public async Task<WebApiCallBack> UserToCashList(int userId = 0, int page = 1, int limit = 10, int status = 0)
        {
            var jm = new WebApiCallBack();

            var where = PredicateBuilder.True<CoreCmsUserTocash>();
            if (status > 0)
            {
                where = where.And(p => p.status == status);
            }
            if (userId > 0)
            {
                where = where.And(p => p.userId == userId);
            }
            var list = await _dal.QueryPageAsync(where, p => p.createTime, OrderByType.Desc, page, limit);
            if (list.Any())
            {
                foreach (var item in list)
                {
                    item.statusName = EnumHelper.GetEnumDescriptionByValue<GlobalEnumVars.UserTocashStatus>(item.status);
                    item.cardNumber = UserHelper.BankCardNoFormat(item.cardNumber);
                }
            }
            jm.status = true;
            jm.data = list;
            jm.otherData = new
            {
                list.TotalPages
            };
            return jm;
        }

        /// <summary>
        /// 提现审核
        /// </summary>
        /// <param name="id"></param>
        /// <param name="status"></param>
        /// <returns></returns>
        public async Task<WebApiCallBack> Examine(int id = 0, int status = 0, int type = (int)GlobalEnumVars.UserTocashType.银行线下转账)
        {
            var jm = new WebApiCallBack();

            var info = await _dal.QueryByClauseAsync(p => p.id == id && (p.status == (int)GlobalEnumVars.UserTocashStatus.待审核 || p.status == (int)GlobalEnumVars.UserTocashStatus.提现异常));
            if (info == null)
            {
                jm.msg = "没有此记录或不是待审核状态";
                return jm;
            }

            switch (type)
            {
                case (int)GlobalEnumVars.UserTocashType.银行线下转账 when status > 0:
                    {
                        var bl = await _dal.UpdateAsync(p => new CoreCmsUserTocash() { status = status, updateTime = DateTime.Now, type = type }, p => p.id == id && (p.status == (int)GlobalEnumVars.UserTocashStatus.待审核 || p.status == (int)GlobalEnumVars.UserTocashStatus.提现异常));
                        jm.status = bl;
                        jm.data = status;
                        if (bl)
                        {
                            //失败给用户退钱到余额
                            if (status == (int)GlobalEnumVars.UserTocashStatus.提现失败)
                            {
                                var toCashInfo = await _dal.QueryByIdAsync(id);

                                // 提现金额 加 服务费返还
                                var newMoney = toCashInfo.money + toCashInfo.withdrawals;
                                var up = await _userServices.UpdateAsync(p => new CoreCmsUser() { balance = p.balance + newMoney }, p => p.id == toCashInfo.userId);
                                if (up)
                                {
                                    //添加记录
                                    var user = await _userServices.QueryByIdAsync(toCashInfo.userId);

                                    var balance = new CoreCmsUserBalance();
                                    balance.type = (int)GlobalEnumVars.UserBalanceSourceTypes.Tocash;
                                    balance.userId = toCashInfo.userId;
                                    balance.balance = user.balance;
                                    balance.createTime = DateTime.Now;
                                    balance.memo = UserHelper.GetMemo(balance.type, toCashInfo.money);
                                    balance.money = newMoney;
                                    balance.sourceId = id.ToString();

                                    await _userBalanceServices.InsertAsync(balance);
                                }
                            }
                        }

                        break;
                    }
                case (int)GlobalEnumVars.UserTocashType.银行线下转账:
                    jm.msg = GlobalErrorCodeVars.Code10000;
                    jm.status = false;
                    break;
                case (int)GlobalEnumVars.UserTocashType.企业付款到零钱:
                    {
                        var user = await _userServices.QueryByIdAsync(info.userId);
                        if (user == null)
                        {
                            jm.msg = "用户信息获取失败";
                            return jm;
                        }
                        var weChatUserInfo = await _weChatInfoServices.QueryByClauseAsync(p => p.userId == info.userId);
                        if (weChatUserInfo == null)
                        {
                            jm.msg = "微信用户数据获取失败";
                            return jm;
                        }

                        var config = await _weChatPayConfigServices.QueryByClauseAsync(p => p.isDefault == true && p.isEnable == true);
                        if (config == null)
                        {
                            jm.msg = "支付配置信息获取失败";
                            return jm;
                        }
                        //构建linkPay请求配置实体
                        var payOptions = new WeChatPayOptions
                        {
                            AppId = config.appId,
                            MchId = config.mchId,
                            APIKey = config.apiKey,
                            APIv3Key = config.apiV3Key,
                            Certificate = config.certificate,
                            RsaPublicKey = config.rsaPublicKey,
                            SubAppId = config.subAppId,
                            SubMchId = config.subMchId
                        };

                        //按分计算
                        //var amount = Convert.ToInt32((info.money - info.withdrawals) * 100);
                        var amount = Convert.ToInt32(info.money * 100);
                        //企业付款到零钱
                        var request = new WeChatPayPromotionTransfersRequest
                        {
                            PartnerTradeNo = YitIdHelper.NextId().ToString(),
                            OpenId = weChatUserInfo.openid,
                            CheckName = "NO_CHECK",
                            ReUserName = info.accountName,
                            Amount = amount,
                            Desc = "余额提现零钱",
                            SpBillCreateIp = _httpContextAccessor.HttpContext?.Connection.RemoteIpAddress != null ? _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString() : "127.0.0.1"
                        };
                        var response = await _v2Client.ExecuteAsync(request, payOptions);
                        if (response.ReturnCode == WeChatPayCode.Success && response.ResultCode == WeChatPayCode.Success)
                        {
                            status = (int)GlobalEnumVars.UserTocashStatus.提现成功;
                            var message = JsonConvert.SerializeObject(response);
                            var bl = await _dal.UpdateAsync(p => new CoreCmsUserTocash()
                            {
                                status = status,
                                updateTime = DateTime.Now,
                                message = message,
                                type = type
                            }, p => p.id == id && (p.status == (int)GlobalEnumVars.UserTocashStatus.待审核 || p.status == (int)GlobalEnumVars.UserTocashStatus.提现异常));
                            jm.status = bl;
                            jm.data = status;
                        }
                        else
                        {
                            status = (int)GlobalEnumVars.UserTocashStatus.提现异常;
                            var message = JsonConvert.SerializeObject(response);
                            var bl = await _dal.UpdateAsync(p => new CoreCmsUserTocash()
                            {
                                status = status,
                                updateTime = DateTime.Now,
                                message = message,
                                type = type
                            }, p => p.id == id && (p.status == (int)GlobalEnumVars.UserTocashStatus.待审核 || p.status == (int)GlobalEnumVars.UserTocashStatus.提现异常));
                            jm.status = bl;
                            jm.data = status;
                        }

                        break;
                    }
                case (int)GlobalEnumVars.UserTocashType.企业付款到银行卡:
                    {
                        var user = await _userServices.QueryByIdAsync(info.userId);
                        if (user == null)
                        {
                            jm.msg = "用户信息获取失败";
                            return jm;
                        }
                        var weChatUserInfo = await _weChatInfoServices.QueryByClauseAsync(p => p.userId == info.userId);
                        if (weChatUserInfo == null)
                        {
                            jm.msg = "微信用户数据获取失败";
                            return jm;
                        }

                        var config = await _weChatPayConfigServices.QueryByClauseAsync(p => p.isDefault == true && p.isEnable == true);
                        if (config == null)
                        {
                            jm.msg = "支付配置信息获取失败";
                            return jm;
                        }
                        //构建linkPay请求配置实体
                        var payOptions = new WeChatPayOptions
                        {
                            AppId = config.appId,
                            MchId = config.mchId,
                            APIKey = config.apiKey,
                            APIv3Key = config.apiV3Key,
                            Certificate = config.certificate,
                            RsaPublicKey = config.rsaPublicKey,
                            SubAppId = config.subAppId,
                            SubMchId = config.subMchId
                        };

                        //按分计算
                        //var amount = Convert.ToInt32((info.money - info.withdrawals) * 100);
                        var amount = Convert.ToInt32(info.money * 100);
                        //企业付款到零钱
                        var request = new WeChatPayPayBankRequest
                        {
                            PartnerTradeNo = YitIdHelper.NextId().ToString(),
                            BankNo = info.cardNumber,
                            TrueName = info.accountName,
                            BankCode = info.accountBank,
                            Amount = amount,
                            Desc = "余额提现银行卡",
                        };
                        var response = await _v2Client.ExecuteAsync(request, payOptions);
                        if (response.ReturnCode == WeChatPayCode.Success && response.ResultCode == WeChatPayCode.Success)
                        {
                            status = (int)GlobalEnumVars.UserTocashStatus.提现成功;
                            var message = JsonConvert.SerializeObject(response);
                            var bl = await _dal.UpdateAsync(p => new CoreCmsUserTocash() { status = status, updateTime = DateTime.Now, message = message, type = type }, p => p.id == id && (p.status == (int)GlobalEnumVars.UserTocashStatus.待审核 || p.status == (int)GlobalEnumVars.UserTocashStatus.提现异常));
                            jm.status = bl;
                            jm.data = status;
                        }
                        else
                        {
                            status = (int)GlobalEnumVars.UserTocashStatus.提现异常;
                            var message = JsonConvert.SerializeObject(response);
                            var bl = await _dal.UpdateAsync(p => new CoreCmsUserTocash() { status = status, updateTime = DateTime.Now, message = message, type = type }, p => p.id == id && (p.status == (int)GlobalEnumVars.UserTocashStatus.待审核 || p.status == (int)GlobalEnumVars.UserTocashStatus.提现异常));
                            jm.status = bl;
                            jm.data = status;
                        }

                        break;
                    }
                case (int)GlobalEnumVars.UserTocashType.商家转账到零钱:
                    {
                        var user = await _userServices.QueryByIdAsync(info.userId);
                        if (user == null)
                        {
                            jm.msg = "用户信息获取失败";
                            return jm;
                        }
                        var weChatUserInfo = await _weChatInfoServices.QueryByClauseAsync(p => p.userId == info.userId);
                        if (weChatUserInfo == null)
                        {
                            jm.msg = "微信用户数据获取失败";
                            return jm;
                        }

                        var config = await _weChatPayConfigServices.QueryByClauseAsync(p => p.isDefault == true && p.isEnable == true);
                        if (config == null)
                        {
                            jm.msg = "支付配置信息获取失败";
                            return jm;
                        }
                        //构建linkPay请求配置实体
                        var payOptions = new WeChatPayOptions
                        {
                            AppId = config.appId,
                            MchId = config.mchId,
                            APIKey = config.apiKey,
                            APIv3Key = config.apiV3Key,
                            Certificate = config.certificate,
                            RsaPublicKey = config.rsaPublicKey,
                            SubAppId = config.subAppId,
                            SubMchId = config.subMchId
                        };

                        //按分计算
                        //var amount = Convert.ToInt32((info.money - info.withdrawals) * 100);
                        var amount = Convert.ToInt32(info.money * 100);

                        var model = new WeChatPayTransferBatchesBodyModel
                        {
                            AppId = payOptions.AppId,
                            BatchName = info.id + "用户提现处理",
                            BatchRemark = info.id + "用户提现处理",
                            TotalAmount = amount,
                            TotalNum = 1,
                            OutBatchNo = "ut" + info.createTime.ToString("yyyyMMddHHmmss"),
                            TransferDetailList = new List<TransferDetail>()
                            {
                                new()
                                {
                                    OutDetailNo = "ut"+ info.createTime.ToString("yyyyMMddHH") + info.id,
                                    TransferAmount = amount,
                                    TransferRemark = "用户提现处理",
                                    OpenId = weChatUserInfo.openid,
                                    UserName = info.accountName
                                }
                            }
                        };

                        var request = new WeChatPayTransferBatchesRequest();
                        request.SetBodyModel(model);

                        var response = await _v3Client.ExecuteAsync(request, payOptions);
                        if (response.IsError == false)
                        {
                            status = (int)GlobalEnumVars.UserTocashStatus.提现成功;
                            var message = JsonConvert.SerializeObject(response);
                            var bl = await _dal.UpdateAsync(p => new CoreCmsUserTocash() { status = status, updateTime = DateTime.Now, message = message, type = type }, p => p.id == id && (p.status == (int)GlobalEnumVars.UserTocashStatus.待审核 || p.status == (int)GlobalEnumVars.UserTocashStatus.提现异常));
                            jm.status = bl;
                            jm.data = status;
                        }
                        else
                        {
                            status = (int)GlobalEnumVars.UserTocashStatus.提现异常;
                            var message = JsonConvert.SerializeObject(response);
                            var bl = await _dal.UpdateAsync(p => new CoreCmsUserTocash() { status = status, updateTime = DateTime.Now, message = message, type = type }, p => p.id == id && (p.status == (int)GlobalEnumVars.UserTocashStatus.待审核 || p.status == (int)GlobalEnumVars.UserTocashStatus.提现异常));
                            jm.status = bl;
                            jm.data = status;
                        }
                        break;
                    }

                default:
                    jm.msg = "提现方式获取失败";
                    jm.status = false;
                    break;
            }
            return jm;


        }


        #region 重写根据条件查询分页数据
        /// <summary>
        ///     重写根据条件查询分页数据
        /// </summary>
        /// <param name="predicate">判断集合</param>
        /// <param name="orderByType">排序方式</param>
        /// <param name="pageIndex">当前页面索引</param>
        /// <param name="pageSize">分布大小</param>
        /// <param name="orderByExpression"></param>
        /// <param name="blUseNoLock">是否使用WITH(NOLOCK)</param>
        /// <returns></returns>
        public async Task<IPageList<CoreCmsUserTocash>> QueryPageAsync(Expression<Func<CoreCmsUserTocash, bool>> predicate,
            Expression<Func<CoreCmsUserTocash, object>> orderByExpression, OrderByType orderByType, int pageIndex = 1,
            int pageSize = 20, bool blUseNoLock = false)
        {
            return await _dal.QueryPageAsync(predicate, orderByExpression, orderByType, pageIndex, pageSize, blUseNoLock);
        }
        #endregion

    }
}
